package org.imsoss.icscf;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.ListIterator;

import javax.naming.Context;
import javax.naming.InitialContext;

import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.RouteHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

import javax.slee.ActivityContextInterface;
import javax.slee.SbbContext;
import javax.slee.facilities.TimerFacility;
import javax.slee.nullactivity.NullActivityContextInterfaceFactory;
import javax.slee.nullactivity.NullActivityFactory;

import org.apache.log4j.Logger;

import org.mobicents.slee.resource.sip.SipActivityContextInterfaceFactory;
import org.mobicents.slee.resource.sip.SipResourceAdaptorSbbInterface;
import org.mobicents.slee.runtime.ActivityContext;
//import org.mobicents.slee.services.sip.common.SipSendErrorResponseException;
import org.mobicents.slee.services.sip.location.LocationSbbLocalObject;
import org.mobicents.slee.services.sip.location.LocationServiceException;
import org.mobicents.slee.services.sip.location.RegistrationBinding;

import org.imsoss.sbb.CommonSbb;
import org.imsoss.util.ImsConfiguration;

public abstract class InterrogatingSbb extends CommonSbb{
	
	public void onServiceStarted(
			javax.slee.serviceactivity.ServiceStartedEvent serviceEvent,
			ActivityContextInterface aci) {
		logger.debug("Starting I-CSCF node of the imsoss ;) sexy ");
		System.out.println("Starting I-CSCF node of the imsoss ;) sexy ");
	}

	public void onRegisterEventDummy(javax.sip.RequestEvent event, ActivityContextInterface aci) {
		syslog("Check this in I-CSCF");
	}
	/**
	 * Event handler for the SIP MESSAGE from the UA
	 * @param event
	 * @param aci
	 */
	public void onRegisterEvent(javax.sip.RequestEvent event, ActivityContextInterface aci) {
		ServerTransaction serverTx = event.getServerTransaction();
		setRegisterServerTransaction(serverTx);
		syslog("Check the ServerTransaction here:::::::: "+serverTx);
		registerTx = serverTx;
		boolean icscfPassed = false;
		boolean pcscfPassed = false;
		Request request = event.getRequest();		
		//logger.debug("This is the entry point of the I-CSCF of the imsoss");
		//System.out.println("This is the entry point of the I-CSCF of the imsoss"+request);		
		ListIterator<ViaHeader> iter = request.getHeaders(ViaHeader.NAME);
		while(iter.hasNext()){
			ViaHeader viaHeader = iter.next();
			//System.out.println("viaHeader in i-cscf--------------->>>>:::"+viaHeader.getHost()+":"+viaHeader.getPort());
			if(viaHeader.getPort() == ImsConfiguration.icscfPort) // Indicates that the SIP messages has passed I-CSCF.
			{
				icscfPassed = true;
				//break;
			}
			if(viaHeader.getPort() == ImsConfiguration.pcscfPort){
				pcscfPassed = true;
			}
		}
		if(pcscfPassed && !icscfPassed){
			
			
			System.out.println("[3]I-CSCF request as "+request);
			System.out.println("[4]I-CSCF processing is required, this needs to talk to the HSS");
			/*
			 * Here we need to start using the Diameter Protocol stuff....
			 * http://groups.google.com/group/mobicents-public/browse_thread/thread/942c1a7dc4213bda
			 * http://code.google.com/p/mobicents/wiki/MobicentsDiameterRA
			 * http://mobicents.googlecode.com/svn/trunk/servers/diameter/examples/base/sbb/src/main/java/org/mobicents/slee/examples/diameter/DiameterExampleSbb.java
			 */

			// Going to generate the SIP Register message to S-CSCF.
			System.out.println("[4.1]SIP message are going to passed to S-CSCF now ");			
			ViaHeader viaHeaderr = (ViaHeader)event.getRequest().getHeader(ViaHeader.NAME);
			//syslog("brach header is "+viaHeaderr.getBranch());
			// peerHostPort is the IP address when the REGISTER MESSAGE should go.
			//String peerHostPort = "127.0.0.1:6090";
			String peerHostPort = ImsConfiguration.pcscfAddress+":"+ImsConfiguration.scscfPort;
			try
			{				
				FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
				
				ToHeader toHeader = (ToHeader)request.getHeader(ToHeader.NAME);
				String toUser = toHeader.getName();
				String toDisplayName = toHeader.getAddress().getDisplayName();
				//System.out.println("toDisplayName ::::: "+toDisplayName);
							
				SipURI requestURI = addressFactory.createSipURI(toDisplayName,peerHostPort);
				CallIdHeader callIdHeader = provider.getNewCallId();
				CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L,Request.REGISTER);
	
				ArrayList viaHeaders = new ArrayList();
				String ipAddress = "127.0.0.1";
				ViaHeader viaHeader = headerFactory.createViaHeader(ipAddress,ImsConfiguration.icscfPort,"udp",null);
				viaHeaders.add(viaHeader);
				viaHeader = headerFactory.createViaHeader(ipAddress,5061,"udp",org.imsoss.refpoint.gm.GmReferencePoint.UEBRANCH);//null);
				viaHeaders.add(viaHeader);
				viaHeader = headerFactory.createViaHeader(ipAddress, ImsConfiguration.pcscfPort, "udp", viaHeaderr.getBranch());
				viaHeaders.add(viaHeader);
				
				RouteHeader oldRouteHeader = (RouteHeader)request.getHeader(RouteHeader.NAME);
				Address routeAddress = oldRouteHeader.getAddress();
				
				URI requesturi = routeAddress.getURI();
				syslog("REQUESTURI :"+requesturi+":");
				
				// Create a new MaxForwardsHeader
				MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
				Request icscfRequest = messageFactory.createRequest(requesturi,
						Request.REGISTER, callIdHeader, cSeqHeader, fromHeader,
						toHeader, viaHeaders, maxForwards);

				// Adding Route Header here, this will basically decide where the SIP message should be dispatched next....
				Address address = addressFactory.createAddress("sip:"+peerHostPort);			
				RouteHeader routeHeader = headerFactory.createRouteHeader(address);
				
				icscfRequest.addHeader(routeHeader);

				
				syslog("------------->>>>> \n"+icscfRequest);
				ClientTransaction registerTid = provider.getNewClientTransaction(icscfRequest);
				//syslog("--->"+ registerTid.getBranchId());	
				// Get the Activity associated with registerTid.......
				
				syslog("Going to Bind to Activity !!! ");
				ActivityContextInterface aciCtx = sipAciFact.getActivityContextInterface(registerTid);
				syslog("Going to Bind to Activity !!! "+aciCtx);
				aciCtx.attach(sbbContext.getSbbLocalObject());		
				syslog("Going to send the Request !!! ");
				registerTid.sendRequest();							
			}
			catch(SipException exp){
				System.out.println("Error in i-CSCF "+exp);
			}
			catch(ParseException exp){
				System.out.println("Error in i-CSCF "+exp);
			}
			catch(InvalidArgumentException exp){
				System.out.println("Error in i-CSCF "+exp);
			}
			catch(javax.slee.UnrecognizedActivityException exp){
				System.out.println("Error in i-CSCF "+exp);
			}
		}
		else{
			System.out.println("I-CSCF processing is NOT!!!! required....");
			return;
		}
	
		javax.sip.ServerTransaction serverTransaction = event.getServerTransaction();		
		// This server transaction should be stored int the ActiviyContext and later reused to send OK respone which would appear from s-cscf.
		// http://forums.java.net/jive/thread.jspa?messageID=335077
		// Storing serverTransaction in CMP.
	}
	
	public void onOkEvent(javax.sip.ResponseEvent event, ActivityContextInterface aci){
		getServerTxs();
		syslog("[7] i-cscf listens to OK event from the s-cscf");
		//syslog(event.getResponse());
		ServerTransaction serverTx = getRegisterServerTransaction();
		ClientTransaction clientTx = event.getClientTransaction();
		syslog("[7.1] The ServerTransaction to send the data back to P-CSCF "+ serverTx);
		syslog("--->"+registerTx);
		syslog("[8] We(i-cscf) needs to send the OK to P-CSCF node");
		boolean shouldProcess = containsIcscf(event.getResponse());
		if(shouldProcess)
		{
			try{
				Response response = event.getResponse();
				ListIterator<ViaHeader> iter = response.getHeaders(ViaHeader.NAME);			
				while(iter.hasNext()){
					ViaHeader viaHeader = iter.next();
					// Via would contain i-cscf IP if the response does not pass from it.
					if(viaHeader.getPort() == ImsConfiguration.icscfPort){
						String peerHostPort = ImsConfiguration.pcscfAddress+":"+ImsConfiguration.pcscfPort;						
						syslog("send response to "+peerHostPort);	
						syslog("response before removing Via route header \n"+response);
						iter.remove();// This will remove i-cscf address from the via header in reponse.			
						syslog("response after removing Via route header \n"+response);
						serverTx.sendResponse(response);
						break;
					}
					else{
						// Ignore!!!
					}
				}
			}
			catch(Exception e){
				logger.error("Error Appearing from onOkEvent() "+e);
				e.printStackTrace();
			}
		}
		else
		{
			syslog(" i-cscf had processed the OK event, simply don't do anything here");
		}				
	}
	
	private boolean containsIcscf(Response response){
		boolean isPresent = false;
		ListIterator<ViaHeader> iter = response.getHeaders(ViaHeader.NAME);
		try{
			while(iter.hasNext()){
				ViaHeader viaHeader = iter.next();
				if(viaHeader.getPort() == ImsConfiguration.icscfPort){
					isPresent = !isPresent;
					break;
				}
			}
		}
		catch(Exception e){
			syslog("failed in containsIcscf"+e);
		}
		return isPresent;
	}
	
	public void getServerTxs(){
		ActivityContextInterface[] acis = sbbContext.getActivities();
		for(ActivityContextInterface aci : acis){
			ClientTransaction stx  = (ClientTransaction)aci.getActivity();
			syslog("Client Transaction is "+stx);
		}		
	}
	
	/**
	 *  Initialize the component
	 */
	public void setSbbContext(SbbContext context) { 

		this.sbbContext = context;
		try {
	            Context myEnv = (Context) new InitialContext().lookup("java:comp/env");
	                        
	            // Getting SLEE Factility
	            timerFacility = (TimerFacility) myEnv.lookup("slee/facilities/timer");
	            nullACIFactory = (NullActivityContextInterfaceFactory)myEnv.
	            				lookup("slee/nullactivity/activitycontextinterfacefactory");
	            nullActivityFactory = (NullActivityFactory)myEnv.lookup("slee/nullactivity/factory");
	            
	            sipAciFact = (SipActivityContextInterfaceFactory)myEnv.lookup("slee/resources/jainsip/1.2/acifactory");
	            // Getting JAIN SIP Resource Adaptor interfaces            
	            fp = (SipResourceAdaptorSbbInterface) myEnv.lookup("slee/resources/jainsip/1.2/provider");
	            
	            provider = fp.getSipProvider();
	            
	            addressFactory = fp.getAddressFactory();
	            headerFactory = fp.getHeaderFactory();
	            messageFactory = fp.getMessageFactory();
            
        	} catch (Exception ne) {
           		logger.error("Failed to set sbb context",ne);
        	}		
	}

    public abstract org.imsoss.icscf.InterrogatingSbbActivityContextInterface asSbbActivityContextInterface(ActivityContextInterface aci);
 
	private SbbContext sbbContext; 
	
	private MessageFactory messageFactory;
	private SipProvider provider;
	private SipResourceAdaptorSbbInterface fp;
	private TimerFacility timerFacility;
	private NullActivityContextInterfaceFactory nullACIFactory;
	private NullActivityFactory nullActivityFactory;
	private AddressFactory addressFactory;
	private HeaderFactory headerFactory;
	
	private static ServerTransaction registerTx;
	// CMP fields here.
	
	private SipActivityContextInterfaceFactory sipAciFact = null;
	
	/* accessor methods for Server Transaction CMP field */
	public abstract ServerTransaction getRegisterServerTransaction();
	public abstract void setRegisterServerTransaction(ServerTransaction serverTx);

	private static final Logger logger = Logger.getLogger(InterrogatingSbb.class); 
	
}
